#include "stdlib.hpp"
#include <purewell/types.hpp>
#include <purewell/messagequeue.hpp>

namespace purewell
{

PWMessageQueue::PWMessageQueue()
{
}

PWMessageQueue::~PWMessageQueue()
{
	if ( -1 != m_fd )
	{
		mq_close((mqd_t)m_fd);
		m_fd = -1;
	}
}

void
PWMessageQueue::destroy(void)
{
	if ( -1 != m_fd )
	{
		mq_close((mqd_t)m_fd);
		m_fd = -1;
	}
}

bool
PWMessageQueue::initialize(const char* name, int omode, int perm)
{
	mqd_t fd((mqd_t)-1);
	if ( omode & O_CREAT )
	{
		fd = mq_open(name, omode, perm, NULL);
	}
	else
	{
		fd = mq_open(name, omode);
	}

	if ( (mqd_t)-1 == fd )
	{
		return false;
	}

	m_name = name;
	m_fd = (int)fd;

	return true;
}

bool
PWMessageQueue::initialize(const char* name, int omode, int perm,
	size_t maxmsg, size_t msgsize)
{
	mqd_t fd((mqd_t)-1);
	if ( omode & O_CREAT )
	{
		struct mq_attr attr;
		memset(&attr, 0x00, sizeof(attr));
		attr.mq_maxmsg = maxmsg;
		attr.mq_msgsize = msgsize;
		attr.mq_curmsgs = 0;
		fd = mq_open(name, omode, perm, &attr);
	}
	else
	{
		fd = mq_open(name, omode);
	}

	if ( (mqd_t)-1 == fd )
	{
		return false;
	}

	m_name = name;
	m_fd = (int)fd;

	return true;
}

bool
PWMessageQueue::unlink(void)
{
	const bool res(0 == (int)mq_unlink(m_name.c_str()));
	m_name.clear();
	return res;
}

ssize_t
PWMessageQueue::send(const void* buf, size_t blen, size_t priority)
{
	return ((mqd_t)0 == mq_send((mqd_t)m_fd, (const char*)buf, blen,
		priority) ? (ssize_t)blen : -1 );
}

ssize_t
PWMessageQueue::sendTimeLimit(const void* buf, size_t blen,
	size_t priority, size_t msec)
{
	struct timespec ts;
	ts.tv_sec = msec / 1000;
	ts.tv_nsec = (msec % 1000) * 1000;
	return ((mqd_t)0 == mq_timedsend((mqd_t)m_fd, (const char*)buf, blen,
		priority, &ts) ? (ssize_t)blen : -1 );
}

ssize_t
PWMessageQueue::receive(void* buf, size_t blen, size_t* priority)
{
	unsigned int prior(0);
	const ssize_t res( mq_receive((mqd_t)m_fd, (char*)buf, blen, &prior) );
	if ( priority ) *priority = (size_t)prior;
	return res;
}

ssize_t
PWMessageQueue::receiveTimeLimit(void* buf, size_t blen,
	size_t* priority, size_t msec)
{
	struct timespec ts;
	ts.tv_sec = msec / 1000;
	ts.tv_nsec = (msec % 1000) * 1000;
	unsigned int prior(0);
	const bool res ( mq_timedreceive((mqd_t)m_fd,
		(char*)buf, blen, &prior, &ts));
	if ( priority ) *priority = (size_t)prior;
	return res;
}

size_t
PWMessageQueue::getCurrentCount(void) const
{
	size_t ret((size_t)-1);
	getStatus(NULL,NULL,&ret);
	return ret;
}

size_t
PWMessageQueue::getMaxCount(void) const
{
	size_t ret((size_t)-1);
	getStatus(&ret,NULL,NULL);
	return ret;
}

size_t
PWMessageQueue::getMessageSize(void) const
{
	size_t ret((size_t)-1);
	getStatus(NULL,&ret,NULL);
	return ret;
}

bool
PWMessageQueue::getStatus(size_t* __restrict__ maxcount,
	size_t* __restrict__ msgsize, size_t* __restrict__ curcount) const
{
	struct mq_attr attr;
	if ( -1 == mq_getattr(m_fd, &attr) ) return false;

	if ( maxcount ) *maxcount = attr.mq_maxmsg;
	if ( msgsize ) *msgsize = attr.mq_msgsize;
	if ( curcount ) *curcount = attr.mq_curmsgs;

	return true;
}

ssize_t
PWMessageQueue::read(void* buf, size_t blen)
{
	return receive(buf, blen, NULL);
}

ssize_t
PWMessageQueue::write(const void* buf, size_t blen)
{
	return send(buf, blen, 0);
}

};//namespace purewell

